using System.Data;
using System.Linq;
using NHibernate;
using NHibernate.Context;
using NHibernate.Engine;
using NHibernate.Linq;

namespace TelerikMvcGridCustomBindingHelper.NHibernate.ORMIntegration
{
    internal class NHibernateIntegration : INHibernateIntegration
    {
        private readonly ISessionFactory _sessionFactory;
        private ISession _session;
        private ISession CurrentSession
        {
            get
            {
                _session = _session ?? GetCurrentSession();
                return _session;
            }
        }

        private ISession GetCurrentSession()
        {
            var factoryImplementor = _sessionFactory as ISessionFactoryImplementor;

            if (factoryImplementor == null || factoryImplementor.CurrentSessionContext == null)
            {
                return _sessionFactory.OpenSession();
            }

            try
            {
                if (CurrentSessionContext.HasBind(_sessionFactory))
                {
                    return _sessionFactory.GetCurrentSession();
                }
            }
            catch (HibernateException)
            {
            }

            var session = _sessionFactory.OpenSession();

            try
            {
                CurrentSessionContext.Bind(session);
            }
            catch (HibernateException)
            {
            }

            return session;
        }

        public NHibernateIntegration(ISessionFactory sessionFactory)
        {
            _sessionFactory = sessionFactory;
        }

        public void BeginTransaction(IsolationLevel isolationLevel)
        {
            if (CurrentSession == null ||
                CurrentSession.Transaction.WasCommitted ||
                CurrentSession.Transaction.WasRolledBack ||
                CurrentSession.Transaction.IsActive)
                return;

            CurrentSession.BeginTransaction(isolationLevel);
        }

        public void CommitTransaction()
        {
            if (CurrentSession == null ||
                CurrentSession.Transaction.WasCommitted ||
                CurrentSession.Transaction.WasRolledBack)
                return;

            CurrentSession.Transaction.Commit();

            Dispose();
        }

        public void RollBackTransaction()
        {
            if (CurrentSession == null ||
                CurrentSession.Transaction.WasCommitted ||
                CurrentSession.Transaction.WasRolledBack)
                return;

            CurrentSession.Transaction.Rollback();

            Dispose();
        }

        public IQueryable<TEntity> Query<TEntity>()
        {
            return CurrentSession.Query<TEntity>();
        }

        public IQueryOver<TEntity, TEntity> QueryOver<TEntity>() where TEntity : class
        {
            return CurrentSession.QueryOver<TEntity>();
        }

        public void Dispose()
        {
            if (_session != null)
            {
                _session.Close();
                _session.Dispose();
            }

            _session = null;
        }
    }
}